#include "Dispatch.h"
#include "dispatchluabindings.h"
#include "../core/Sheet.h"

RegisterCppObj<Dispatch> g_register_dispatch_object;

// Dispatch specific stuff
void DispatchGO::sendMessage(const std::string& target, IData& msg)
{
	// this is a bit of a kludge (that is, setting a field)
	msg.sets(msg.named("msgType"), target);

	if(_targetMap.count(target) > 0)
	{
		for(unsigned int i = 0; i < _targetMap[target].size(); ++i)
		{
			if(_targetMap[target][i] != NULL)
				_targetMap[target][i]->recMessage(target, msg);
		}
	}
	//// Iterate through, send message to every receiver which has target as a prefix
	//// (e.g., if target = "sh:", it will send to "sh:1", "sh:graphics", and so on)

	//std::map<std::string, std::vector<DispatchTarget*>>::iterator itr;
	//for(itr = _targetMap.begin(); itr != _targetMap.end();)
	//{
	//	if((*itr).first.length() >= target.length() && (*itr).first.substr(0, target.length()) == target)
	//	{
	//		(*itr).second->recMessage((*itr).first, msg);
	//	}
	//	++itr;
	//	//(*itr)->update(dt);
	//}
}

void DispatchGO::addTarget(DispatchTarget* tg, std::string& targetAlias)
{
	if(_targetMap.count(targetAlias) == 0)
	{
		std::vector<DispatchTarget*> temp;
		_targetMap[targetAlias] = temp;
	}

	_targetMap[targetAlias].push_back(tg);
}

void DispatchGO::removeAlias(const std::string& alias, DispatchTarget* tg)
{
	if(_targetMap.count(alias) > 0)
	{
		std::vector<DispatchTarget*>& tempo = _targetMap[alias];
		std::vector<DispatchTarget*>::iterator i2 = tempo.begin();
		while(i2 != tempo.end())
		{
			if((*i2) == tg)
			{
				i2 = tempo.erase(i2);
			}
			else
				++i2;
		}
	}
}

void DispatchGO::removeTarget(DispatchTarget* tg)
{
	std::map<std::string, std::vector<DispatchTarget*> >::iterator itr;
	for(itr = _targetMap.begin(); itr != _targetMap.end();)
	{
		std::vector<DispatchTarget*>& tempo = (*itr).second;
		
		if(tempo.size() == 0)
		{
			std::map<std::string, std::vector<DispatchTarget*> >::iterator previtr = itr;
			++itr;
			_targetMap.erase(previtr);
		}
		else
		{
			removeAlias((*itr).first, tg);
			++itr;
		}
		//(*itr)->update(dt);
	}
}

// General global object stuff
std::string DispatchGO::getClassName()
{
	return "DispatchGO";
}

void DispatchGO::updateFromUM(double dt)
{
	// no particular update call
}

//std::map<std::string, DispatchTarget*> _targetMap;
//std::vector<std::string> _targetNamesList;
//std::vector<DispatchTarget*> _targetList;



Dispatch::Dispatch()
{
	// nothing special here either..
	_dgo = NULL;
	_sink = new AccumulatorDT;
}

// Dispatch specific stuff
void Dispatch::sendMessage(std::string& target, IData& msg)
{
	getDGO();
	if(_dgo)
		_dgo->sendMessage(target, msg);
}

void Dispatch::addHandler(std::string& recName, AnyAOPtr obj)
{
	std::string nullStr("");
	addNamedHandler(recName, nullStr, obj);
}

void Dispatch::addNamedHandler(std::string& recName, std::string& hName, AnyAOPtr obj)
{
	getDGO();
	if(_dgo)
	{

		_handlerMap[recName] = std::pair<AnyAOPtr, std::string>(obj, hName);
		_dgo->addTarget(_sink, recName);
	}
}

void Dispatch::removeHandler(std::string& recName)
{
	getDGO();
	if(_dgo)
	{
		if(_handlerMap.count(recName) > 0)
			_handlerMap.erase(recName);
		_dgo->removeAlias(recName, _sink);
	}
}

// General abstract object stuff goes below

// Return the c++ type id
int Dispatch::getCppTypeId()
{
	//FIXME
	return 0;
}

// Returns whether this can be cast to a given type
bool Dispatch::isType(int id)
{
	//FIXME
	return false;
}

// Return the c++ type as a string 
std::string Dispatch::getCppTypeName()
{
	return "Dispatch";
}

// returns a description of this thing
std::string Dispatch::getDescription()
{
	return "Dispatch: send messages between sheets and elsewhere";
}

// create another one of these
AbstractObject* Dispatch::newInstance()
{
	return new Dispatch;
}

// init & update
void Dispatch::init()
{
	// nothing special
}

void Dispatch::update(double dt)
{
	std::vector<std::string>* tns = NULL;
	std::vector<IData>* msgs = NULL;
	_sink->getLists(tns, msgs);

	for(unsigned int i = 0; i < tns->size() && i < msgs->size(); ++i)
	{
		if(_handlerMap.count((*tns)[i]) > 0 && *(_handlerMap[(*tns)[i]].first) != NULL)
		{
			std::string& dname = _handlerMap[(*tns)[i]].second;
			if(dname == "")
				IData bla = _handlerMap[(*tns)[i]].first->interact("dispatch_message", (*msgs)[i]);
			else
				IData bla = _handlerMap[(*tns)[i]].first->interact(dname, (*msgs)[i]);
			//InterData::release(bla);
		}
		else
			LOG(logERROR) << "Recvd message for handler " << (*tns)[i] << " which does not exist.";
	}

	delete tns;
	delete msgs;
}

// push its static functions into the given lua table
void Dispatch::pushStaticFuncs(lua_State* L)
{
	// superclass static functions...
	AbstractObject::pushStaticFuncs(L);

	// auto interface generated stuff...
	DispatchLuaBindings::pushStaticFuncs(L);
}

Dispatch::~Dispatch()
{
	if(_dgo)
		_dgo->removeTarget(_sink);
	delete _sink;
}

void Dispatch::getDGO()
{
	if(_dgo == NULL)
	{
		if(getSheet() != NULL && getSheet()->ultra() != NULL)
		{
			GlobalObject* temp = getSheet()->ultra()->getGlobalObject("DispatchGO");
			if(temp != NULL)
			{
				_dgo = dynamic_cast<DispatchGO*>(temp);
			}
			else
			{
				_dgo = new DispatchGO;
				getSheet()->ultra()->addGlobalObject(_dgo, "DispatchGO");
			}
		}
	}
}

//AccumulatorDT _sink;
//std::map<std::string, AnyAOPtr> _handlerMap;
